/*
 * Phys2D - a 2D physics engine based on the work of Erin Catto. The
 * original source remains:
 * 
 * Copyright (c) 2006 Erin Catto http://www.gphysics.com
 * 
 * This source is provided under the terms of the BSD License.
 * 
 * Copyright (c) 2006, Phys2D
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met:
 * 
 *  * Redistributions of source code must retain the above 
 *    copyright notice, this list of conditions and the 
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above 
 *    copyright notice, this list of conditions and the following 
 *    disclaimer in the documentation and/or other materials provided 
 *    with the distribution.
 *  * Neither the name of the Phys2D/New Dawn Software nor the names of 
 *    its contributors may be used to endorse or promote products 
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */
package my2Cents;

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;

import my2Cents.tools.*;
import net.phys2d.math.ROVector2f;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.BasicJoint;
import net.phys2d.raw.Body;
import net.phys2d.raw.BodyList;
import net.phys2d.raw.CollisionEvent;
import net.phys2d.raw.ConstrainingJoint;
import net.phys2d.raw.ElasticJoint;
import net.phys2d.raw.FixedJoint;
import net.phys2d.raw.Joint;
import net.phys2d.raw.JointList;
import net.phys2d.raw.SpringJoint;
import net.phys2d.raw.StaticBody;
import net.phys2d.raw.World;
import net.phys2d.raw.shapes.AABox;
import net.phys2d.raw.shapes.PBox;
import net.phys2d.raw.shapes.PCircle;
import net.phys2d.raw.shapes.PConvexPolygon;
import net.phys2d.raw.shapes.PDynamicShape;
import net.phys2d.raw.shapes.PPolygon;
import net.phys2d.raw.shapes.PhysShape;
import net.phys2d.raw.test.AbstractDemo;

/**
 * A stacking demonstration with balls!
 * 
 * @author Kevin Glass
 */
public class Sandbox extends AbstractDemo {
	/** The world in which the simulation takes place */
	private static World staticworld;
	//private Body ceiling;
	private boolean mouseDown = false;
	private boolean mouseDown2 = false;
	private int mouseX = 0;
	private int mouseY = 0;
	private int mouseStartX = -1;
	private int mouseStartY = -1;
	private int lastMouseX = -1;
	private int lastMouseY = -1;
	private int rawMouseX = -1;
	private int rawMouseY = -1;
	private int mtimer;
	private boolean key_ctrl = false;
	private boolean key_shift = false;
	private double zoomSpd = 0;
	//private char currentKey = 'm';
	//private char defaultTool = 'm';
	private Vector tools = new Vector();
	public static int BODYSTATES_SANDBOX = 0;
	public static int BODYSTATES_SANDBOX_WR = 2;
	public static boolean makeStatic = false;
	public static int G_CURTIME = 0;
	public static Vector messageStack;
	private static ToolMenu toolMenu;
	private static JScrollPane toolSP;
	public static JComponentList toolList;
	public static int selectedTool = 0;
	public static boolean toolMenuOpen = false;
	public static Output out;
	public static Player localPlayer;
	public static AffineTransform camera;
	private float cam_transx = 0;
	private float cam_transy = 0;
	private boolean shouldLoadTools = false;
	private static Vector<KeyEvent> keysDown = new Vector<KeyEvent>();
	private static Canvas staticSimCanvas;
	public static String VERSION = "12b";
	private boolean help = true;
	private static Vector<KeyTrigger> keyTriggers = new Vector<KeyTrigger>();
	private String BetaMessage = "This beta has a few bugs:\n\n" +
			"- Sometimes the toolmenu won't finish loading,\n" +
			"press SHIFT+R to reload it.\n\n" +
			"- Sometimes the top menu bar won't load,\n" +
			"resize the main window to fix it.\n\n" +
			"That is all.\n\n\n" +
			"-Zak.";
	/**
	 * Create the demo
	 */
	public Sandbox() {
		super("Physics Sandbox v1.2a -beta");
		
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		GO = false;
		camera = new AffineTransform();
		messageStack = new Vector();
		out = new Output();
		//out.setVisible(true);
	}
	
	public void start() {
		world.getBodyStateManager().initBodyStateVector(0);
		world.getBodyStateManager().initBodyStateVector(1);
		staticworld = world;
		super.start();
	}
	
	public boolean GO() {
		return GO;
	}
	
	public static UndoList getUndoList() {
		return staticworld.getUndoList();
	}
	
	public static void undoLast() {
		Undo unDone = getUndoList().undoLast();
		if(unDone != null) {
			Msg("Undone: " + unDone.getUndoText(),"undo.jpg");
		}		
	}
	
	private void LoadFromClasses() {
		try {
			//addTool(new BombTool('B',this));
			//addTool(new MoverTool('m',this));
			Class[] classes = Util.getClasses("my2Cents.tools",this.getClass());
			for(int i=0; i<classes.length; i++) {
				Class cl = classes[i];
				Object instance;
				if(cl.getModifiers() == 1) {
					try {
						instance = cl.newInstance();
						if(instance instanceof AbstractPhysTool) {
							addTool((AbstractPhysTool) instance);
						}
					} catch (InstantiationException e1) {
						// TODO Auto-generated catch block
						out.println("Failed To Add Tool: " + cl.getName());
						out.println("" + cl.getModifiers());
						e1.printStackTrace();
					} catch (IllegalAccessException e1) {
						// TODO Auto-generated catch block
						out.println("Failed To Add Tool: " + cl.getName());
						out.println("" + cl.getModifiers());
						e1.printStackTrace();
					}
				}
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}

	@SuppressWarnings("unchecked")
	private void LoadTools() {
		toolMenu = new ToolMenu(frame);
		toolMenu.open();
		addTool(new BasicJointTool());
		addTool(new BombTool());
		addTool(new BoxTool());
		addTool(new CircleTool());
		addTool(new CopyTool());
		addTool(new FixedJointTool());
		addTool(new GearTool());
		addTool(new GearToolInv());
		addTool(new LineTool());
		addTool(new MotorTool());
		addTool(new MoverTool());
		addTool(new NoCollideTool());
		addTool(new PhysProps());
		addTool(new PolyDrawTool());
		addTool(new RemoverField());
		addTool(new RemoverTool());
		addTool(new RopeTool());
		addTool(new WinchTool());
		addTool(new ShapeBreaker());
		addTool(new SpringTool());
		addTool(new ThrusterTool());
		addTool(new SpawnerTool());
		
		try {
			Thread.sleep(520);
		} catch (InterruptedException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}

		if(toolMenu != null) {
			toolMenu.close();
		}
	}
	
	private AbstractPhysTool getTool(int toolIndex) {
		if(tools.size() >= toolIndex && 
			tools.get(toolIndex) != null) {
			AbstractPhysTool tool = (AbstractPhysTool) tools.get(toolIndex);
			return tool;
		}
		return null;
	}

	public static void addKeyTrigger(KeyTrigger t) {
		keyTriggers.add(t);
	}
	
	public static void removeKeyTrigger(KeyTrigger t) {
		keyTriggers.remove(t);
	}
	
	protected static void fireKeyTriggers(int key, int state) {
		for(int i=0; i<keyTriggers.size(); i++) {
			KeyTrigger t = keyTriggers.get(i);
			if(t == null) continue;
			if(t.getKeyCode() == key) {
				if(state == 1) t.keyPressed();
				if(state == 0) t.keyReleased();
			}
		}
	}
	
	public static void emulateKey(int code, boolean pressed) {
		if(pressed) {
			boolean contain = false;
			for(int i=0; i<keysDown.size(); i++) {
				KeyEvent other = keysDown.get(i);
				if(other.getKeyCode() == code) {
					contain = true;
				}
			}
			if(!contain && code != 'q') {
				keysDown.add(new KeyEvent(null,KeyEvent.KEY_PRESSED,0,0,code,(char)code));
				fireKeyTriggers(code,1);
			}
		} else {
			for(int i=0; i<keysDown.size(); i++) {
				KeyEvent other = keysDown.get(i);
				if(other.getKeyCode() == code) {
					keysDown.remove(i);
					fireKeyTriggers(code,0);
				}
			}
		}
	}
	
	protected void addListeners(Component c) {
		c.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent arg0) {
				mouseX = arg0.getX();
				mouseY = arg0.getY();
				rawMouseX = mouseX;
				rawMouseY = mouseY;
			}
			public void mouseDragged(MouseEvent arg0) {
				rawMouseX = arg0.getX();
				rawMouseY = arg0.getY();
				
				arg0 = fixForCamera(arg0);
				int x = arg0.getX();
				int y = arg0.getY();
				
				if(mouseDown) {
					AbstractPhysTool tool = getTool(selectedTool);
					if(tool.hasThinkDelay()) {
						if(mtimer > 5) tool.toolThink(arg0);
					} else {
						tool.toolThink(arg0);
					}
				}
				if(mouseDown2) {
					if(key_shift) {
						zoomSpd += ((float) (y - lastMouseY)/1000);
					} else {
						cam_transx += (float) (x - lastMouseX)/5;
						cam_transy += (float) (y - lastMouseY)/5;
					}
				}
				
				lastMouseX = x;
				lastMouseY = y;
				//key_shift
			}			
		});
		c.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent arg0) {
				arg0 = fixForCamera(arg0);
				
				lastMouseX = arg0.getX();
				lastMouseY = arg0.getY();
				mouseStartX = arg0.getX();
				mouseStartY = arg0.getY();
				
				if(arg0.getButton() == MouseEvent.BUTTON1) {
					mouseDown = true;
					mtimer = 0;
					getTool(selectedTool).startTool(arg0);
				} else {
					mouseDown2 = true;
				}
			}

			public void mouseReleased(MouseEvent arg0) {
				arg0 = fixForCamera(arg0);
				if(arg0.getButton() == MouseEvent.BUTTON1) {
					mouseDown = false;
					getTool(selectedTool).endTool(arg0);
				} else {
					mouseDown2 = false;
					//undoLast();
				}
				mouseStartX = -1;
				mouseStartY = -1;
			}
		});
		c.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent arg0) {
				//localPlayer.keyPressed(arg0);
				System.out.println(arg0.getKeyCode());
				if(arg0.getKeyCode() == 17) {
					key_ctrl = true;
				}
				if(arg0.getKeyCode() == 16) {
					key_shift = true;
				}
				if(getTool(arg0.getKeyChar()) != null) {
					//currentKey = arg0.getKeyChar();
				}
				if(arg0.getKeyChar() == 'q' && !toolMenuOpen) {
					if(toolMenu != null) toolMenu.open();
				}
				if(key_ctrl) {
					if(arg0.getKeyCode() == 83) {
						menu_saveWorld();
					}
					if(arg0.getKeyCode() == 79) {
						menu_loadWorld(false);
					}
					if(arg0.getKeyCode() == 78) {
						menu_newWorld();
					}
					if(arg0.getKeyCode() == 73) {
						menu_loadWorld(true);
					}
					if(arg0.getKeyCode() == 72) {
						help = !help;
					}
				}
				
				boolean contain = false;
				for(int i=0; i<keysDown.size(); i++) {
					KeyEvent other = keysDown.get(i);
					if(other.getKeyCode() == arg0.getKeyCode()) {
						contain = true;
					}
				}
				if(!contain && arg0.getKeyChar() != 'q') {
					keysDown.add(arg0);
					fireKeyTriggers(arg0.getKeyCode(),1);
				}
			}
			public void keyReleased(KeyEvent arg0) {
				//localPlayer.keyReleased(arg0);
				if(arg0.getKeyCode() == 17) {
					key_ctrl = false;
				}
				if(arg0.getKeyCode() == 16) {
					key_shift = false;
				}
				for(int i=0; i<keysDown.size(); i++) {
					KeyEvent other = keysDown.get(i);
					if(other.getKeyCode() == arg0.getKeyCode()) {
						keysDown.remove(i);
						fireKeyTriggers(arg0.getKeyCode(),0);
					}
				}
			}
		});
	}
	
	public static boolean keyIsDown(char c) {
		for(int i=0; i<keysDown.size(); i++) {
			KeyEvent event = keysDown.get(i);
			if(event.getKeyChar() == c) {
				return true;
			}
		}
		return false;
	}
	
	public static boolean keyIsDown(int c) {
		for(int i=0; i<keysDown.size(); i++) {
			KeyEvent event = keysDown.get(i);
			if(event.getKeyCode() == c) {
				return true;
			}
		}
		return false;
	}
	
	protected void draw(Graphics2D g) {
		G_CURTIME++;
		
		int width = simCanvas.getWidth();
		int height = simCanvas.getHeight();
		
		for(int i=0; i<messageStack.size(); i++) {
			ScrMessage msg = (ScrMessage) messageStack.get(i);
			msg.drawBG(g);
		}
		
		AffineTransform prev = g.getTransform();
		cameraThink(g);
		super.draw(g);
		if(getTool(selectedTool) != null) {
			AbstractPhysTool t = getTool(selectedTool);
			t.drawGhost(g);
		}
		g.setTransform(prev);
		g.setColor(Color.black);
		/*for(int i=0; i<tools.size(); i++) {
			AbstractPhysTool p = (AbstractPhysTool) tools.get(i);
			if(currentKey == p.getKey()) {g.setColor(Color.RED);}
			g.drawString(p.getKey() + " - " + p.getName(), 15, (i+5)*15);
			g.setColor(Color.black);
		}*/
		if(getTool(selectedTool) != null) {
			AbstractPhysTool t = getTool(selectedTool);
			g.drawString("Current Tool: " + t.getName(), 15, height - 70);
			//t.drawGhost(g);
		}
		g.setColor(Color.BLACK);
		
		int y = 60;
		if(help) {
			g.drawString("[CONTROL + H] to toggle this help.",15,y); y+=15;
			y+=15;
			g.drawString("Press Q for ToolMenu.",15,y); y+=15;
			g.drawString("Press Z to Undo.",15,y); y+=15;
			g.drawString("RightClick to move the camera.",15,y); y+=15;
			g.drawString("Shift + RightClick to zoom the camera.",15,y); y+=15;
			g.drawString("Press R to reset the camera.",15,y); y+=15;
			g.drawString("Press Shift + R to reset the simulation.",15,y); y+=15;
			g.drawString("Press Shift + C to remove all bodies.",15,y); y+=15;
			g.drawString("Press SpaceBar to start and stop the simulation",15,y); y+=15;
			y += 15;
			g.drawString("[CONTROL + S] to save the current world.",15,y); y+=15;
			g.drawString("[CONTROL + O] to open a saved world.",15,y); y+=15;
			g.drawString("[CONTROL + I] to import a saved world.",15,y); y+=15;
			g.drawString("[CONTROL + N] to create a new world.",15,y); y+=15;
		}
		
		//Zak Blystone
		g.drawString("Physics Sandbox, by Zachary Blystone",width - 250,height - 25);
		g.drawString("TNS Rocks!",width - 250,height - 10);
		
		/*String codes = "";
		for(int i=0; i<keysDown.size(); i++) {
			codes = codes + "[" + keysDown.get(i).getKeyChar() + "]";
		}
		g.drawString(codes,610,490);*/
		
		int m = 0;
		for(int i=0; i<messageStack.size(); i++) {
			ScrMessage msg = (ScrMessage) messageStack.get(i);
			msg.setCount(messageStack.size());
			msg.setIndex(m);
			if(!msg.zipped()) {
				m++;
			}
			msg.think();
			msg.draw(g);
			msg.setTSize(simCanvas.getWidth(), simCanvas.getHeight());
			if(msg.shouldRemove()) {
				messageStack.remove(msg);
			}
		}
	}
	
	private void cameraThink(Graphics2D g) {
		if(frame != null) {
		int w = frame.getWidth();
		int h = frame.getHeight();
		//int px = (int) localPlayer.getPosition().getX();
		//int py = (int) localPlayer.getPosition().getY();
		//int cx = (int) camera.getTranslateX() - frame.getWidth()/2;
		//int cy = (int) camera.getTranslateY() - frame.getHeight()/2;
		//double deltax = ((cx + px)*.02);
		//double deltay = ((cy + py)*.02);
		//camera.translate(-(deltax), -(deltay));
		double nmx = mouseX;
		double nmy = mouseY;
		
		camera.translate(nmx, nmy);
		
		if(camera.getScaleX() > 1.8 && camera.getScaleY() > 1.8) {
			zoomSpd = -0.05;
		}
		else if(camera.getScaleX() < 0.2 && camera.getScaleY() < 0.2) {
			zoomSpd = 0.05;
		}
		camera.scale(1+zoomSpd, 1+zoomSpd);
		
		camera.translate(-nmx, -nmy);
		
		cam_transx = Math.min(cam_transx, 20);
		cam_transy = Math.min(cam_transy, 20);
		
		cam_transx = Math.max(cam_transx, -20);
		cam_transy = Math.max(cam_transy, -20);
		
		
		camera.translate(
				cam_transx * (1/camera.getScaleX()), 
				cam_transy * (1/camera.getScaleY()));
		
		cam_transx += -cam_transx*0.2;
		cam_transy += -cam_transy*0.2;
		zoomSpd += -zoomSpd*0.2;
		
		g.setTransform(camera);
		}
	}
	
//	public static void translateToCameraSpace(Vector2f vec) {
//		float x = vec.x;
//		float y = vec.y;
//		x = (float) (x * 2-camera.getScaleX());
//		y = (float) (y * 2-camera.getScaleY());
//		x -= camera.getTranslateX();
//		y -= camera.getTranslateY();
//	}
	
	private MouseEvent fixForCamera(MouseEvent arg0) {
		int x = arg0.getX();
		int y = arg0.getY();
		
		x -= getCameraPos().getX();
		y -= getCameraPos().getY();
	
		x*=(1/camera.getScaleX());
		y*=(1/camera.getScaleY());
		
		MouseEvent ne = new MouseEvent(this.simCanvas, 0, 0, 0, x, y, 0, false, arg0.getButton());
		return ne;
	}
	
	public static Vector2f getCameraPos() {
		double tx = camera.getTranslateX();
		double ty = camera.getTranslateY();
		
		return new Vector2f((float) tx, 
							(float) ty);
	}
	
	public static Vector2f getCameraScale() {
		return new Vector2f((float) camera.getScaleX(),
							(float) camera.getScaleY());
	}

	protected void update() {
		mtimer++;
		BodyList list = world.getBodies();
		for(int i=0; i<list.size(); i++) {
			Body b = list.get(i);
			AABox bounds = b.getShape().getBounds();
			float boundx = bounds.getOffsetX() + bounds.getWidth()/2;
			float boundy = bounds.getOffsetY() + bounds.getHeight()/2;
			Vector2f vec = (Vector2f) b.getLastPosition();
			Vector2f min = new Vector2f(-30.0f,-30.0f);
			Vector2f max = new Vector2f(this.simCanvas.getWidth()+30, this.simCanvas.getHeight()+30);
			min.x -= camera.getTranslateX();
			min.y -= camera.getTranslateY();
			max.x -= camera.getTranslateX();
			max.y -= camera.getTranslateY();
		}
		JointList joints = world.getJoints();
		for(int i=0; i<joints.size(); i++) {
			Joint j = joints.get(i);
			if(j.doBreak()) {
				world.remove(j);
			}
		}
		
		runMaxToolThink();
	}
	
	private void runMaxToolThink() {
		AbstractPhysTool tool = getTool(selectedTool);
		if(tool.getThinkLevel() == AbstractPhysTool.THINKLEVEL_MAX) {
			Point pos = simCanvas.getMousePosition();
			int x,y;
			if(pos != null) {x = pos.x; y = pos.y;
				MouseEvent e = new MouseEvent(simCanvas, MouseEvent.BUTTON1, 0, 0, x, y, 0, mouseDown);
				if(tool.hasThinkDelay()) {
					if(mtimer > 5) tool.toolThink(fixForCamera(e));
				} else  {
					tool.toolThink(fixForCamera(e));
				}
			}
		}		
	}

	/**
	 * @see net.phys2d.raw.test.AbstractDemo#keyHit(char)
	 */
	
	protected void keyHit(char c) {
		if (c == 'R') {
			shouldLoadTools = true;
			camera.setToTranslation(0, 0);
			camera.setToScale(1, 1);
			super.keyHit('r');
		}
		if (c == 'r') {
			camera.setToTranslation(0, 0);
			camera.setToScale(1, 1);
		}
		if (c == 'c') {
			super.keyHit(c);
		}
		if (c == 'C') {
			getUndoList().undoAll();
		}
		if (c == 'z') {
			undoLast();
		}
		if (c == ' ') {
			if(GO == false) {
				world.getBodyStateManager().saveBodyStates(BODYSTATES_SANDBOX,world);
				GO = true;
			} else {
				/*Vector list = new Vector();
				for(int i=0; i<world.getBodies().size(); i++) {
					Body body = world.getBodies().get(i);
					if(!BodyState.bodyStored(BODYSTATES_SANDBOX,body)) {
						list.add(body);
					}
				}
				undoList.undo(list.toArray());*/
				//BodyState.restoreBodyStates(BODYSTATES_SANDBOX,world);
				GO = false;
			}
		}
	}
	
	/**
	 * @see net.phys2d.raw.test.AbstractDemo#init(net.phys2d.raw.World)
	 */
	protected void init(World world) {
		this.world = world;
		UndoList undoList = getUndoList();
		if(undoList == null) {undoList = new UndoList(world);}
		undoList.undoAll();
		Body body1 = new StaticBody("Ground1", new PBox(700.0f, 20.0f));
		body1.setPosition(400.0f, 430);
		body1.setFriction(1);
		world.add(body1);
		
		if(shouldLoadTools) {
			LoadTools();
		}
		shouldLoadTools = false;
		
		//localPlayer = new Player();
		//localPlayer.setPosition(100, 400);
		//world.add(localPlayer);
	}
	
	public void onToolEnd() {
		mouseDown = false;
	}

	
	/**
	 * Entry point for testing
	 * 
	 * @param argv The arguments to the test
	 */
	public static void main(String[] argv) {
		Util_FILE.checkMainFolder();
		Sandbox demo = new Sandbox();
		demo.start();
	}

	public Body createBody(PhysShape shape, float mass, boolean makeStatic) {
		Body b = new Body(shape, mass);
		if(makeStatic) {
			b = new StaticBody(shape);
			b.setDrawColor(new Color(0,155,50));
		}
		return b;
	}
	
	public void postBodyCreated(Body body) {
		Vector2f pos = (Vector2f) body.getPosition();
		body.doOutlineFx();
		world.add(body);
		String cl = body.getShape().getName();
		getUndoList().add(new Undo(body, world, cl));
		if(this.GO) {
			//BodyState.saveSingleBodyState(BODYSTATES_SANDBOX,world,body);
		}
	}

	public static World getWorld() {
		return staticworld;
	}
	
	public static boolean isRunning() {
		return GO;
	}
	
	private void makeMessage(String txt, String icon) {
		Msg(txt,icon);
	}

	public static void Msg(String txt, String icon) {
		int w = staticSimCanvas.getWidth();
		int h = staticSimCanvas.getHeight();
		messageStack.add(new ScrMessage(txt, messageStack.size(),w,h,icon));
	}
	
	private void addTool(AbstractPhysTool tool) {
		final int id = tools.size();
		tool.SetSandbox(this);
		tool.setID(id);
		if(toolMenu != null) {
			toolMenu.addTool(tool);
		}
		tools.add(tool);
		try {
			Thread.sleep(60);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static ToolMenu getToolMenu() {
		return toolMenu;
	}
	
	private void menuListeners(JMenu menu) {
		menu.addMenuListener(new MenuListener() {
			public void menuCanceled(MenuEvent arg0) {
				// TODO Auto-generated method stub
				System.out.println("Canceled");
			}
			public void menuDeselected(MenuEvent arg0) {
				// TODO Auto-generated method stub
				System.out.println("DeSelected");
				simCanvas.setVisible(true);
			}
			public void menuSelected(MenuEvent arg0) {
				// TODO Auto-generated method stub
				System.out.println("Selected");
				simCanvas.setVisible(false);
			}
		});
	}
	
	private void menu_saveWorld() {
		simCanvas.setVisible(false);
		File f = Util_FILE.chooseFile(frame, true, "world_" + VERSION, new File("data/worlds"));
		if(f != null) {
			boolean success = Util.saveWorld(f, world);
			if(success) {
				makeMessage("World saved to file: " + f.getName() + ".","save.jpg");
			} else {
				makeMessage("Error: Failed to save world.","error.jpg");
			}
		}
		simCanvas.setVisible(true);
		simCanvas.requestFocus();
		frame.repaint();
	}
	
	private void menu_loadWorld(boolean importWorld) {
		WorldLoader loader = new WorldLoader(frame.getOwner(),"Load",ModalityType.APPLICATION_MODAL);
		loader.setVisible(true);
		System.out.println("Loader Finished");
		//simCanvas.setVisible(false);
		//Util_FILE.chooseFile(frame, false, "world");
		//simCanvas.setVisible(true);
		
		if(loader.getSelectedFile() != null) {
			try {
				World newWorld = Util.loadWorld(loader.getSelectedFile());
				if(newWorld == null) {
					makeMessage("Error: Failed to load world.","error.jpg");
					return;
				}
				if(!importWorld) {
					world.terminate();
				}
				makeMessage("Loaded World From File.","load.jpg");
				world.merge(newWorld);
				world.step();
			} catch (Exception e) {
				e.printStackTrace();
				makeMessage("Error: Failed to load world.","error.jpg");
			}
		}
		frame.invalidate();
		frame.repaint();
		simCanvas.requestFocus();
	}
	
	private void menu_newWorld() {
		world.terminate();
		init(world);
		frame.invalidate();
		frame.repaint();
		simCanvas.requestFocus();
	}
	
	protected void postGui() {
		staticSimCanvas = simCanvas;
		
		JMenu file = new JMenu("File");
		menuListeners(file);
		
		JMenuItem file_new = new JMenuItem("New");
		file_new.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				menu_newWorld();
			}
		});
		file.add(file_new);
		
		JMenuItem file_save = new JMenuItem("Save");
		file_save.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				menu_saveWorld();
			}
		});
		file.add(file_save);
		
		JMenuItem file_load = new JMenuItem("Load");
		file_load.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				menu_loadWorld(false);
			}
		});
		file.add(file_load);
		
		JMenuItem file_import = new JMenuItem("Import");
		file_import.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				menu_loadWorld(true);
			}
		});
		file.add(file_import);
		
		menubar.add(file);
		frame.invalidate();
		
		//JOptionPane.showMessageDialog(frame, BetaMessage, "Read This:", JOptionPane.INFORMATION_MESSAGE);
		LoadTools();
	}
}
